package com.deluxelx.soul.inn.service.counselor.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deluxelx.soul.inn.bean.chat.ChatRoom;
import com.deluxelx.soul.inn.bean.counselor.*;
import com.deluxelx.soul.inn.bean.user.User;
import com.deluxelx.soul.inn.common.param.CollectedParam;
import com.deluxelx.soul.inn.common.util.*;
import com.deluxelx.soul.inn.common.util.supportIds.SnowflakeId;
import com.deluxelx.soul.inn.mapper.common.IndexDataSupport;
import com.deluxelx.soul.inn.mapper.counselor.CounselorMapper;
import com.deluxelx.soul.inn.mapper.user.UserMapper;
import com.deluxelx.soul.inn.service.article.ArticleService;
import com.deluxelx.soul.inn.service.chat.ChatRoomService;
import com.deluxelx.soul.inn.service.common.FollowRelationService;
import com.deluxelx.soul.inn.service.counselor.*;
import com.deluxelx.soul.inn.service.course.CourseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

import static com.deluxelx.soul.inn.common.constant.CounselorConstant.*;
import static com.deluxelx.soul.inn.common.constant.IndexType.COUNSELOR;

/**
 * 咨询师服务实现类
 *
 * @author: zihao.liao
 * @date: 2024/1/17 14:15
 */
@Service
@Slf4j
public class CounselorServiceImpl extends ServiceImpl<CounselorMapper, Counselor> implements CounselorService {

    @Resource
    private ExpertiseService expertiseService;

    @Resource
    private ExpertiseLinkService expertiseLinkService;

    @Resource
    private GradeService gradeService;

    @Resource
    private AppointmentService appointmentService;

    @Resource
    private UserMapper userMapper;

    @Resource
    private ArticleService articleService;

    @Resource
    private CourseService courseService;

    @Resource
    private ChatRoomService chatRoomService;

    @Resource
    private FollowRelationService followRelationService;

    @Resource
    private SnowflakeId snowflake;

    @Resource
    private IndexDataSupport indexDataSupport;

    @Resource
    private RedisUtil redisUtil;

    private static List<ScheduleTime> scheduleTimeList = new ArrayList<>();

    @Override
    public List<CounselorVO> fetchIndexCounselorList() {
        List<String> counselorIndexIds = indexDataSupport.selectIndexDataByType(COUNSELOR);
        if (CollectionUtil.isEmpty(counselorIndexIds)) {
            return Collections.emptyList();
        }
        List<Counselor> counselorList = (List<Counselor>) this.listByIds(counselorIndexIds);
        return generateCounselorVOS(counselorList);
    }

    @Override
    public List<CounselorVO> fetchSearchCounselorList(String keyword) {
        List<Counselor> counselorList = this.lambdaQuery()
                .like(StrUtil.isNotBlank(keyword), Counselor::getName, keyword)
                .eq(Counselor::getDeleteFlag, 0)
                .list();
        return generateCounselorVOS(counselorList);
    }

    @Override
    public List<CounselorFollowVO> fetchMyFollowCounselorList(String userId) {
        List<String> counselorIds = followRelationService.getMyFollowIds(userId, COUNSELOR_FOLLOW_KEY);
        if (CollectionUtil.isEmpty(counselorIds)) {
            return Collections.emptyList();
        }
        List<Counselor> counselorList = (List<Counselor>) this.listByIds(counselorIds);
        List<CounselorFollowVO> followVOList = new ArrayList<>();
        counselorList.forEach(item -> {
            CounselorFollowVO followVO = new CounselorFollowVO();
            String counselorId = item.getId();
            followVO.setId(counselorId);
            followVO.setName(item.getName());
            followVO.setAvatar(item.getAvatar());
            followVO.setRankValue(item.getRankValue());
            Integer chatCount = redisUtil.get(buildChatCounselorKey(counselorId), Integer.class);
            if (chatCount == null) {
                chatCount = item.getChatCount();
            }
            Long year = DateTimeUtil.getEmploymentTimeYear(item.getEmploymentTime());
            followVO.setLevel(SpecialNumUtil.getCounselorLevel(chatCount, year));
            followVOList.add(followVO);
        });
        return followVOList;
    }

    @Override
    public List<AppointmentVO> fetchMyAppointmentList(String userId, Integer status) {
        List<Appointment> appointmentList = appointmentService.lambdaQuery()
                .eq(Appointment::getUserId, userId)
                .eq(Appointment::getStatus, status)
                .orderByDesc(Appointment::getCreateTime)
                .list();
        if (CollectionUtil.isEmpty(appointmentList)) {
            return null;
        }
        List<String> idList = appointmentList.stream().map(Appointment::getCounselorId).collect(Collectors.toList());
        List<Counselor> counselorList = (List<Counselor>) this.listByIds(idList);
        Map<String, Counselor> map = new HashMap<>();
        counselorList.forEach(item -> map.put(item.getId(), item));

        List<AppointmentVO> result = new ArrayList<>();
        appointmentList.forEach(appointment -> {
            Counselor counselor = map.get(appointment.getCounselorId());
            AppointmentVO appointmentVO = new AppointmentVO();
            appointmentVO.setId(appointment.getId());
            appointmentVO.setCounselorId(appointment.getCounselorId());
            appointmentVO.setAvatar(counselor.getAvatar());
            appointmentVO.setName(counselor.getName());
            appointmentVO.setGrade(counselor.getRankValue());
            appointmentVO.setLevel(
                    SpecialNumUtil.getCounselorLevel(
                            counselor.getChatCount(),
                            DateTimeUtil.getEmploymentTimeYear(counselor.getEmploymentTime())
                    )
            );
            String createTime = DateTimeUtil.getAppointmentCreateTime(appointment.getCreateTime());
            appointmentVO.setCreateTime(createTime);
            appointmentVO.setConsultNum(appointment.getConsultNum());
            appointmentVO.setAppointmentDate(DateTimeUtil.getStandardDate(appointment.getAppointmentTime()));
            appointmentVO.setAppointmentTime(DateTimeUtil.getTime(appointment.getAppointmentTime()));
            result.add(appointmentVO);
        });
        return result;
    }

    @Override
    public List<CounselorVO> fetchByCondition(CounselorQueryParam param) {
        List<Counselor> counselorList;
        if (null == param) {
            counselorList = this.lambdaQuery()
                    .eq(Counselor::getDeleteFlag, 0)
                    .list();
        } else {
            LambdaQueryWrapper<Counselor> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(Counselor::getDeleteFlag, 0);
            if (null != param.getExpertId()) {
                List<String> counselorIdList = expertiseLinkService.lambdaQuery()
                        .eq(ExpertiseLink::getStatus, 1)
                        .eq(ExpertiseLink::getExpertiseId, param.getExpertId())
                        .list()
                        .stream()
                        .map(ExpertiseLink::getCounselorId)
                        .collect(Collectors.toList());
                queryWrapper.in(CollectionUtil.isNotEmpty(counselorIdList), Counselor::getId, counselorIdList);
            }
            if (null != param.getGradeId()) {
                queryWrapper.eq(Counselor::getRankValue, GRADE_MAP.get(param.getGradeId()));
            }
            counselorList = this.list(queryWrapper);
        }
        return generateCounselorVOS(counselorList);
    }

    @Override
    public List<Grade> fetchGradeList() {
        String key = GRADE_CACHE_KEY;
        List<Grade> gradeListCache = redisUtil.lGetAll(key, Grade.class);
        if (gradeListCache != null) {
            return gradeListCache;
        }
        List<Grade> gradeList = gradeService.list();
        redisUtil.lSet(key, gradeList);
        return gradeList;
    }

    @Override
    public List<Expertise> fetchExpertiseList() {
        String key = EXPERT_CACHE_KEY;
        List<Expertise> expertListCache = redisUtil.lGetAll(key, Expertise.class);
        if (CollectionUtil.isNotEmpty(expertListCache)) {
            return expertListCache;
        }
        List<Expertise> expertiseList = expertiseService.list();
        redisUtil.lSet(key, expertiseList);
        return expertiseList;
    }

    @Override
    public CounselorDetailVO fetchCounselorDetail(String counselorId, String userId) {
        Counselor counselor = this.getById(counselorId);
        CounselorDetailVO counselorDetailVO = null;
        if (null != counselor) {
            counselorDetailVO = new CounselorDetailVO();
            counselorDetailVO.setId(counselor.getId());
            counselorDetailVO.setName(counselor.getName());
            counselorDetailVO.setAvatar(counselor.getAvatar());
            counselorDetailVO.setRankValue(counselor.getRankValue());
            counselorDetailVO.setIntroduce(counselor.getIntroduce());
            // 获取咨询师详情特有的属性
            counselorDetailVO.setExpertiseList(expertiseService.fetchCounselorDetailExpertise(counselorId));
            counselorDetailVO.setArticleList(articleService.fetchArticleListByCounselor(counselorId));
            counselorDetailVO.setCourseList(courseService.fetchCourseListByCounselor(counselorId));
            // 获取该用户与咨询师的聊天室id，为null则之前未咨询过
            ChatRoom entity = chatRoomService.lambdaQuery()
                    .eq(ChatRoom::getId, EncryptionUtil.chatRoomEncrypt(userId, counselorId))
                    .eq(ChatRoom::getDeleteFlag, 0)
                    .one();
            if (null != entity) {
                counselorDetailVO.setChatRoomId(entity.getId());
            }
        }
        return counselorDetailVO;
    }

    @Override
    public Boolean isCollectCounselor(CollectedParam param) {
        if (param == null || StrUtil.isEmpty(param.getUserId()) || StrUtil.isEmpty(param.getCounselorId())) {
            return Boolean.FALSE;
        }
        String key = buildFollowCounselorKey(param);
        Integer flag = redisUtil.get(key, Integer.class);
        if (null == flag) {
            Integer isSuccess = followRelationService.checkFollow(param);
            if (isSuccess > 0) {
                redisUtil.set(key, 1, COUNSELOR_CACHE_EXPIRE_TIME);
                return Boolean.TRUE;
            } else {
                return Boolean.FALSE;
            }
        } else if (0 == flag) {
            return Boolean.FALSE;
        } else if (1 == flag) {
            return Boolean.TRUE;
        }
        return Boolean.FALSE;
    }

    @Override
    public Boolean followCounselor(CollectedParam param) {
        if (param == null || StrUtil.isEmpty(param.getUserId()) || StrUtil.isEmpty(param.getCounselorId())) {
            return Boolean.FALSE;
        }
        try {
            String key1 = buildFollowCounselorKey(param);
            boolean bool1 = redisUtil.set(key1, 1, COUNSELOR_CACHE_EXPIRE_TIME);
            String key2 = buildFollowCounselorKey(param.getCounselorId());
            boolean bool2;
            if (redisUtil.hasKey(key2)) {
                redisUtil.incr(key2, 1);
                bool2 = redisUtil.expire(key2, COUNSELOR_CACHE_EXPIRE_TIME);
            } else {
                Counselor counselor = this.getById(param.getCounselorId());
                if (null == counselor) {
                    return Boolean.FALSE;
                }
                bool2 = redisUtil.set(key2, counselor.getFansCount() + 1, COUNSELOR_CACHE_EXPIRE_TIME);
            }
            return bool1 && bool2;
        } catch (Exception e) {
            log.error("关注咨询师失败：" + e.getMessage());
            return Boolean.FALSE;
        }
    }

    @Override
    public Boolean unFollowCounselor(CollectedParam param) {
        if (param == null || StrUtil.isEmpty(param.getUserId()) || StrUtil.isEmpty(param.getCounselorId())) {
            return Boolean.FALSE;
        }
        try {
            String key1 = buildFollowCounselorKey(param);
            boolean bool1 = false;
            String key2 = buildFollowCounselorKey(param.getCounselorId());
            if (!redisUtil.hasKey(key1)) {
                Integer isSuccess = followRelationService.checkFollow(param);
                if (isSuccess > 0) {
                    bool1 = redisUtil.set(key1, 0, COUNSELOR_CACHE_EXPIRE_TIME);
                }
            } else {
                bool1 = redisUtil.set(key1, 0, COUNSELOR_CACHE_EXPIRE_TIME);
            }
            if (!bool1) {
                return Boolean.FALSE;
            }
            if (redisUtil.hasKey(key2)) {
                redisUtil.decr(key2, 1);
                return redisUtil.expire(key2, COUNSELOR_CACHE_EXPIRE_TIME);
            } else {
                Counselor counselor = this.getById(param.getCounselorId());
                if (null == counselor) {
                    return Boolean.FALSE;
                }
                return redisUtil.set(key2, counselor.getFansCount() - 1, COUNSELOR_CACHE_EXPIRE_TIME);
            }
        } catch (Exception e) {
            log.error("取消关注咨询师失败：" + e.getMessage());
            return Boolean.FALSE;
        }
    }

    @Override
    public Boolean isCounselor(String userId) {
        if (StrUtil.isBlank(userId)) {
            return false;
        }
        Integer count = this.lambdaQuery()
                .eq(Counselor::getUserId, userId)
                .eq(Counselor::getDeleteFlag, 0)
                .count();
        return count > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean certified(CertifiedParam param) {
        LocalDateTime now = LocalDateTime.now();
        String counselorId = snowflake.nextStrId();
        try {
            Counselor counselor = new Counselor();
            counselor.setId(counselorId);
            counselor.setName(param.getName());
            counselor.setAvatar(param.getAvatar());
            counselor.setRankValue(param.getGrade());
            counselor.setIntroduce(param.getIntroduce());
            counselor.setUserId(param.getUserId());
            counselor.setCreateTime(now);
            counselor.setCreateUser(param.getUserId());
            counselor.setUpdateTime(now);
            counselor.setUpdateUser(param.getUserId());
            this.save(counselor);

            List<ExpertiseLink> links = new ArrayList<>();
            for (Integer expertiseId : param.getExpertList()) {
                ExpertiseLink entity = new ExpertiseLink();
                entity.setId(snowflake.nextStrId());
                entity.setCounselorId(counselorId);
                entity.setExpertiseId(expertiseId);
                links.add(entity);
            }
            expertiseLinkService.insertBatch(links);

            LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.set(StrUtil.isNotBlank(param.getTelephone()), User::getTelephone, param.getTelephone())
                    .set(StrUtil.isNotBlank(param.getAvatar()), User::getAvatar, param.getAvatar())
                    .eq(User::getId, param.getUserId());
            userMapper.update(null, updateWrapper);

            redisUtil.sSet(COUNSELOR_CACHE_KEY, counselorId);
            return true;
        } catch (Exception e) {
            log.error("认证咨询师失败：" + e.getMessage());
            return false;
        }
    }

    @Override
    public List<ScheduleTime> fetchScheduleById(String counselorId) {
        if (CollectionUtil.isEmpty(scheduleTimeList)) {
            getOneWeekTime();
        }
        List<ScheduleTime> result = ObjectUtil.deepCopyList(scheduleTimeList, ScheduleTime::clone);
        List<AppointmentBO> appointmentMes = appointmentService.getAppointmentMes(counselorId);
        if (CollectionUtil.isEmpty(appointmentMes)) {
            return result;
        }
        appointmentMes.forEach(mes -> {
            LocalDate today = LocalDate.now();
            int index = (int) ChronoUnit.DAYS.between(today, mes.getAppointmentDate()) - 1;
            ScheduleTime scheduleTime = result.get(index);
            if (mes.getTimeOfDay().equals(MORNING)) {
                scheduleTime.setMorning(1);
            } else if (mes.getTimeOfDay().equals(AFTERNOON)) {
                scheduleTime.setAfternoon(1);
            }
        });
        return result;
    }

    @Override
    public Boolean makeAppointment(AppointmentParam param) {
        if (param == null || StrUtil.isEmpty(param.getCounselorId()) || StrUtil.isEmpty(param.getUserId())
            || param.getAppointmentTime() == null) {
            return Boolean.FALSE;
        }
        Appointment appointment = new Appointment();
        appointment.setCounselorId(param.getCounselorId());
        appointment.setUserId(param.getUserId());
        appointment.setAppointmentTime(param.getAppointmentTime().plusHours(8));  // 暂时就这样解决前后端时间相差8小时问题吧
        LocalDateTime now = LocalDateTime.now();
        appointment.setConsultNum(SpecialNumUtil.getConsultNum(now));
        appointment.setCreateTime(now);
        return appointmentService.saveEntity(appointment);
    }

    private List<CounselorVO> generateCounselorVOS(List<Counselor> counselorList) {
        List<CounselorVO> counselorVOList = new ArrayList<>();
        for (Counselor counselor : counselorList) {
            CounselorVO counselorVO = new CounselorVO();
            String counselorId = counselor.getId();
            counselorVO.setId(counselorId);
            counselorVO.setName(counselor.getName());
            counselorVO.setAvatar(counselor.getAvatar());
            counselorVO.setRankValue(counselor.getRankValue());
            Integer fansCount = redisUtil.get(buildFollowCounselorKey(counselorId), Integer.class);
            counselorVO.setFansCount(fansCount == null ? counselor.getFansCount() : fansCount);
            Integer chatCount = redisUtil.get(buildChatCounselorKey(counselorId), Integer.class);
            if (chatCount == null) {
                chatCount = counselor.getChatCount();
            }
            counselorVO.setChatCount(SpecialNumUtil.getFollowTypeNum(chatCount));
            counselorVO.setCost(counselor.getCost());
            Long year = DateTimeUtil.getEmploymentTimeYear(counselor.getEmploymentTime());
            counselorVO.setEmploymentTimeYear(year);
            counselorVO.setLevel(SpecialNumUtil.getCounselorLevel(chatCount, year));
            counselorVO.setExpertise(expertiseService.fetchIndexCounselorExpertise(counselor.getId()));
            counselorVOList.add(counselorVO);
        }
        return counselorVOList;
    }

    private void getOneWeekTime() {
        LocalDate day = LocalDate.now();
        List<ScheduleTime> list = new ArrayList<>();
        for (int i = 0; i < 7; i++) {
            day = day.plusDays(1);
            ScheduleTime item = new ScheduleTime();
            item.setMonth(String.valueOf(day.getMonthValue()));
            item.setDay(String.valueOf(day.getDayOfMonth()));
            item.setWeekday(WEEKDAY_MAP.get(day.getDayOfWeek().getValue()));
            item.setMorning(0);
            item.setAfternoon(0);
            list.add(item);
        }
        scheduleTimeList = list;
    }

}
